package uz.rt.utils;


import java.util.ArrayList;

/**
 * Coordinate class creates an N dimensional coordinate of type T. Coordinate are comparable with each other because
 * the
 * Comparable interface is implemented. Comparison of Coordinate instances is done based on:
 * <ul>
 * <li>number of dimensions</li>
 * <li>when dimensions are equal: pairwise comparison of each dimension</li>
 * </ul>
 *
 * @param <T> DataType that extends the Comparable interface and an abstract Number
 * @author Wacko von Belsebub
 * @version 1.0.0
 */
//public class Coordinate<T extends Number & Comparable<? super T>> implements Comparable<Coordinate<T>>, Itemizable<Integer, T>
public class Coordinate<T extends Comparable<? super T>> implements Comparable<Coordinate<T>>, Itemizable<Integer, T>
{

    /** Default dimension of a coordinate */
    private static final int DefaultDimension = 2;
    /** List of all the coordinate points */
    protected ArrayList<T> c = null;

    /** Initialise a coordinate by default in 2D */
    public Coordinate()
    {
        c = new ArrayList<T>(DefaultDimension);
    }

    /**
     * Constructor initialises and set the dimensions
     *
     * @param dimensions the amount of coordinate parameters
     */
    public Coordinate(final int dimensions)
    {
        c = new ArrayList<T>(dimensions);
    }

    /**
     * Constructor initialises and sets the coordinates
     *
     * @param list a list of coordinates
     */
    public Coordinate(final ArrayList<T> list)
    {
        c = list;
    }

    /**
     * onstructor initialises and sets the coordinates to a default value
     *
     * @param dimensions   the amount of coordinate parameters
     * @param defaultValue default value of the coordinates
     */
    public Coordinate(final int dimensions, final T defaultValue)
    {
        c = new ArrayList<T>(dimensions);
        for (int i = 0; i < c.size(); i++)
            c.set(i, defaultValue);
    }


    @Override
    public boolean equals(Object o)
    {
        if (this == o) return true;
        if (!(o instanceof Coordinate)) return false;

        Coordinate that = (Coordinate) o;

        return c.equals(that.c);

    }

    @Override
    public int hashCode()
    {
        return c.hashCode();
    }


// --------------------- Interface Comparable ---------------------

    public int compareTo(Coordinate<T> o)
    {
        // compare dimensions
        if (c.size() != o.size()) return Integer.compare(c.size(), o.size());
        // equal dimensions, compare each dimension pairwise, when no difference are found in the end return 0
        for (int i = 0; i < c.size(); i++)
        {
            //noinspection NumberEquality
            if (c.get(i) != o.get(i))
            {
                return c.get(i).compareTo(o.get(i));
            }
        }
        return 0;  //To change body of implemented methods use File | Settings | File Templates.
    }

// --------------------- Interface Itemizable ---------------------

    public void add(final T position)
    {
        c.add(position);
    }

    /**
     * Edit the coordinate in dimension idx
     *
     * @param idx      dimension [0 ... N-1]
     * @param position Coordinate value in the idx dimension
     * @throws ArrayIndexOutOfBoundsException This exception is thrown when the dimension argument is either below 0 or
     *                                        exceeds the total number of dimensions (N-1)
     */
    public void edit(final Integer idx, final T position)
    {
        if (idx < 0) throw new ArrayIndexOutOfBoundsException("Cannot edit coordinate : index out of bound (<0)");
        else if (idx > c.size() - 1)
            throw new ArrayIndexOutOfBoundsException("Cannot edit coordinate : index out of bound (>dimension)");
        else c.set(idx, position);
    }

    public void removeByKey(final Integer idx)
    {
        if (idx < 0) throw new ArrayIndexOutOfBoundsException("Cannot edit coordinate : index out of bound (<0)");
        else if (idx > c.size() - 1)
            throw new ArrayIndexOutOfBoundsException("Cannot edit coordinate : index out of bound (>dimension)");
        c.remove(idx.intValue());
    }

    public void removeValue(final T position)
    {
        c.remove(position);
    }

    /**
     * Get the coordinate in dimension idx
     *
     * @param idx dimension [0 ... N-1]
     * @return Coordinate value in the idx dimension
     * @throws ArrayIndexOutOfBoundsException This exception is thrown when the dimension argument is either below 0 or
     *                                        exceeds the total number of dimensions (N-1)
     */
    public T get(final int idx)
    {
        if (idx < 0) throw new ArrayIndexOutOfBoundsException("Cannot get coordinate : index out of bound (<0)");
        else if (idx > c.size() - 1)
            throw new ArrayIndexOutOfBoundsException("Cannot get coordinate : index out of bound (>dimension)");
        else return c.get(idx);
    }

    /**
     * Returns the number of coordinate dimensions
     *
     * @return Coordinate dimensions
     */
    public int size()
    {
        return c.size();
    }

    @Override
    public String toString()
    {
        StringBuilder sb = new StringBuilder();
        sb.append('[');
        for (int i = 0; i < c.size(); i++)
        {
            sb.append(c.get(i).toString());
            if(i!=c.size()-1) sb.append(", ");
        }
        sb.append(']');
        return sb.toString();
    }
}
